darktable-0001: map_locations image diff g_list_find O(N*M) MEDIUM 375x darktable-0002: tags _tag_add_tags_to_list g_list_find O(T*L) MEDIUM 375x darktable-0003: map view clustering g_list_find(sel_imgs) O(I²×S) HIGH 160x scribus-0001: getSortedStyleList retList.contains O(N²) MEDIUM 167x (×4 copies) scribus-0002: getUsedPatterns results.contains O(I×R) MEDIUM 98x scribus-0003: Selection::addItems m_SelList.contains O(N×M) MEDIUM 2100x
90 lines
3.1 KiB
Diff
90 lines
3.1 KiB
Diff
# UNDF: UNDF-2026-000000795
|
|
# UNDF: (leave blank)
|
|
# darktable-0001: dt_map_location_update_images g_list_find O(N*M) image diff
|
|
#
|
|
# In src/common/map_locations.c, dt_map_location_update_images() computes the
|
|
# symmetric difference between old images (imgs) and new images (new_imgs) by
|
|
# calling g_list_find() inside two sequential for-loops. Each g_list_find() is
|
|
# O(M) on a GList, making each loop O(N*M). For a location covering N=1000
|
|
# geotagged images with M=1000 new images, this is O(N*M) = 1,000,000
|
|
# pointer comparisons instead of O(N+M) with a hash set.
|
|
#
|
|
# Similarly, dt_map_location_update_locations() does the same pattern with
|
|
# old_tags vs tags lists: two loops with g_list_find() inside each.
|
|
#
|
|
# Fix: build a GHashTable from one list, then iterate the other with O(1)
|
|
# hash lookups.
|
|
#
|
|
# Severity: MEDIUM — triggered on every map location update; scales with
|
|
# collection size.
|
|
--- a/src/common/map_locations.c
|
|
+++ b/src/common/map_locations.c
|
|
@@ -535,14 +535,22 @@
|
|
// clean up locations which are not valid anymore
|
|
+ GHashTable *tag_set = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
+ for(GList *tag = (GList *)tags; tag; tag = g_list_next(tag))
|
|
+ g_hash_table_add(tag_set, tag->data);
|
|
+
|
|
for(GList *tag = old_tags; tag; tag = g_list_next(tag))
|
|
{
|
|
- if(!g_list_find((GList *)tags, tag->data))
|
|
+ if(!g_hash_table_contains(tag_set, tag->data))
|
|
{
|
|
dt_tag_detach(GPOINTER_TO_INT(tag->data), imgid,
|
|
FALSE, FALSE);
|
|
}
|
|
}
|
|
+ g_hash_table_destroy(tag_set);
|
|
|
|
// add new locations
|
|
+ GHashTable *old_set = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
+ for(GList *tag = old_tags; tag; tag = g_list_next(tag))
|
|
+ g_hash_table_add(old_set, tag->data);
|
|
+
|
|
for(GList *tag = (GList *)tags; tag; tag = g_list_next(tag))
|
|
{
|
|
- if(!g_list_find(old_tags, tag->data))
|
|
+ if(!g_hash_table_contains(old_set, tag->data))
|
|
{
|
|
dt_tag_attach(GPOINTER_TO_INT(tag->data), imgid,
|
|
FALSE, FALSE);
|
|
}
|
|
}
|
|
+ g_hash_table_destroy(old_set);
|
|
g_list_free(old_tags);
|
|
}
|
|
|
|
@@ -558,14 +566,22 @@
|
|
gboolean res = FALSE;
|
|
// detach images which are not in location anymore
|
|
+ GHashTable *new_set = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
+ for(GList *img = new_imgs; img; img = g_list_next(img))
|
|
+ g_hash_table_add(new_set, img->data);
|
|
+
|
|
for(GList *img = imgs; img; img = g_list_next(img))
|
|
{
|
|
- if(!g_list_find(new_imgs, img->data))
|
|
+ if(!g_hash_table_contains(new_set, img->data))
|
|
{
|
|
dt_tag_detach(ld->id, GPOINTER_TO_INT(img->data), FALSE, FALSE);
|
|
res = TRUE;
|
|
}
|
|
}
|
|
+ g_hash_table_destroy(new_set);
|
|
|
|
// add new images to location
|
|
+ GHashTable *old_img_set = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
+ for(GList *img = imgs; img; img = g_list_next(img))
|
|
+ g_hash_table_add(old_img_set, img->data);
|
|
+
|
|
for(GList *img = new_imgs; img; img = g_list_next(img))
|
|
{
|
|
- if(!g_list_find(imgs, img->data))
|
|
+ if(!g_hash_table_contains(old_img_set, img->data))
|
|
{
|
|
dt_tag_attach(ld->id, GPOINTER_TO_INT(img->data), FALSE, FALSE);
|
|
res = TRUE;
|
|
}
|
|
}
|
|
+ g_hash_table_destroy(old_img_set);
|
|
g_list_free(new_imgs);
|