# UNDF: UNDF-2026-000000804 # UNDF: (leave blank) # scribus-0003: Selection::addItems m_SelList.contains O(N×M) # # In scribus/selection.cpp, addItems() iterates over the items to add (N) and # for each calls m_SelList.contains(item), which is O(M) on the existing # QList> selection list. Total: O(N×M). For a "Select All" # on a 5000-item document adding to an existing selection of 2000, this is # ~10M pointer comparisons. # # The single-item addItem() also calls m_SelList.contains(item) — O(M) per # call — but is less severe since it adds one item at a time. # # Fix: build a QSet from m_SelList before the loop for O(1) lookup, # or maintain a companion QSet as a class member. # # Severity: MEDIUM — triggered on every multi-select operation (rubber-band, # Select All, group selection); scales with document item count. --- a/scribus/selection.cpp +++ b/scribus/selection.cpp @@ -194,6 +194,12 @@ bool Selection::addItems(const QList& items) { if (items.isEmpty()) return false; + // Build hash set for O(1) membership check instead of O(M) QList::contains + QSet existing; + existing.reserve(m_SelList.count()); + for (int i = 0; i < m_SelList.count(); ++i) + existing.insert(m_SelList.at(i).data()); + QList< QPointer > toAdd; toAdd.reserve(items.count()); for (int i = 0; i < items.count(); ++i) { PageItem* item = items.at(i); - if (m_SelList.contains(item)) + if (existing.contains(item)) continue; toAdd.append(item); + existing.insert(item); item->setSelected(true); }