130 lines
5.2 KiB
Diff
130 lines
5.2 KiB
Diff
# UNDF: UNDF-2026-000001213
|
||
# UNDF: (leave blank — assigned by generate_undf.py)
|
||
# scribus-0004: file saver names.contains O(N²) style filter on save
|
||
#
|
||
# In three Scribus file format savers (scribus150format_save.cpp,
|
||
# scribus170format_save.cpp, scribus171format_save.cpp), the write-style
|
||
# functions call:
|
||
#
|
||
# QList<QString> names = lists.charStyleNames(); // QMap::keys() → QList
|
||
# for (int i = 0; i < styleList.count(); ++i) {
|
||
# if (!names.contains(charStyle.name())) // O(N) linear scan
|
||
# continue;
|
||
# ...
|
||
# }
|
||
#
|
||
# `names` is a QList<QString> created from a QMap's .keys() call. Each
|
||
# call to QList::contains() is O(N). The outer loop is O(S) over all
|
||
# sorted styles, making the overall complexity O(S²) per save operation.
|
||
# This occurs identically for charStyleNames, styleNames (paragraph styles),
|
||
# and in scribus150/170/171 format variants — 6 sites total.
|
||
#
|
||
# Additionally, emF.contains(pi) inside a double loop (selection items ×
|
||
# text items per frame) when collecting embedded frames is O(F×T×E) — a
|
||
# third pattern at the same severity.
|
||
#
|
||
# Fix: replace QList<QString>::contains with QMap<QString,QString>::contains
|
||
# from the underlying resource collection maps (O(log N)) or convert to
|
||
# QSet<QString> upfront (O(1)).
|
||
#
|
||
# The cleanest fix uses the existing QMap directly:
|
||
# lists.charStyles().contains(charStyle.name()) // O(log N)
|
||
#
|
||
# For the embedded frames list, replace QList<PageItem*> emF with a companion
|
||
# QSet<PageItem*> emFSet for O(1) dedup.
|
||
#
|
||
# Severity: MEDIUM — triggered on every file save; scales with style count
|
||
# (O(S²) where S can reach 500+ in professional template documents).
|
||
# A document with 500 paragraph styles + 500 char styles = 500,000 linear
|
||
# scans per save instead of 1,000 log-N map lookups.
|
||
#
|
||
# Affects: scribus/plugins/fileloader/scribus{150,170,171}format/*_save.cpp
|
||
# — 6 contains() sites + 2 emF/embeddedFrames sites
|
||
#
|
||
--- a/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
|
||
+++ b/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
|
||
@@ -96,16 +96,12 @@ void Scribus150Format::writeStyles(QXmlStreamWriter& writer, const ResourceColle
|
||
|
||
// Write character styles
|
||
- QList<QString> names = lists.charStyleNames();
|
||
QList<int> styleList = m_Doc->getSortedCharStyleList();
|
||
for (int i = 0; i < styleList.count(); ++i)
|
||
{
|
||
const CharStyle& charStyle = m_Doc->charStyles()[styleList[i]];
|
||
- if (!names.contains(charStyle.name()))
|
||
+ if (!lists.charStyles().contains(charStyle.name()))
|
||
continue;
|
||
writer.writeStartElement("CHARSTYLE");
|
||
putNamedCStyle(writer, charStyle);
|
||
writer.writeEndElement();
|
||
}
|
||
|
||
// Write paragraph styles
|
||
- names = lists.styleNames();
|
||
styleList = m_Doc->getSortedStyleList();
|
||
for (int i = 0; i < styleList.count(); ++i)
|
||
{
|
||
const ParagraphStyle& paragraphStyle = m_Doc->paragraphStyles()[styleList[i]];
|
||
- if (names.contains(paragraphStyle.name()))
|
||
+ if (lists.styles().contains(paragraphStyle.name()))
|
||
putPStyle(writer, paragraphStyle, "STYLE");
|
||
}
|
||
|
||
--- a/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
|
||
+++ b/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
|
||
@@ -96,16 +96,12 @@ void Scribus170Format::writeStyles(QXmlStreamWriter& writer, const ResourceColle
|
||
|
||
// Write character styles
|
||
- QList<QString> names = lists.charStyleNames();
|
||
QList<int> styleList = m_Doc->getSortedCharStyleList();
|
||
for (int i = 0; i < styleList.count(); ++i)
|
||
{
|
||
const CharStyle& charStyle = m_Doc->charStyles()[styleList[i]];
|
||
- if (!names.contains(charStyle.name()))
|
||
+ if (!lists.charStyles().contains(charStyle.name()))
|
||
continue;
|
||
writer.writeStartElement("CHARSTYLE");
|
||
putNamedCStyle(writer, charStyle);
|
||
writer.writeEndElement();
|
||
}
|
||
|
||
// Write paragraph styles
|
||
- names = lists.styleNames();
|
||
styleList = m_Doc->getSortedStyleList();
|
||
for (int i = 0; i < styleList.count(); ++i)
|
||
{
|
||
const ParagraphStyle& paragraphStyle = m_Doc->paragraphStyles()[styleList[i]];
|
||
- if (names.contains(paragraphStyle.name()))
|
||
+ if (lists.styles().contains(paragraphStyle.name()))
|
||
putPStyle(writer, paragraphStyle, "STYLE");
|
||
}
|
||
|
||
--- a/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp
|
||
+++ b/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp
|
||
@@ -96,16 +96,12 @@ void Scribus171Format::writeStyles(QXmlStreamWriter& writer, const ResourceColle
|
||
|
||
// Write character styles
|
||
- QList<QString> names = lists.charStyleNames();
|
||
QList<int> styleList = m_Doc->getSortedCharStyleList();
|
||
for (int i = 0; i < styleList.count(); ++i)
|
||
{
|
||
const CharStyle& charStyle = m_Doc->charStyles()[styleList[i]];
|
||
- if (!names.contains(charStyle.name()))
|
||
+ if (!lists.charStyles().contains(charStyle.name()))
|
||
continue;
|
||
writer.writeStartElement("CharacterStyle");
|
||
putNamedCStyle(writer, charStyle);
|
||
writer.writeEndElement();
|
||
}
|
||
|
||
// Write paragraph styles
|
||
- names = lists.styleNames();
|
||
styleList = m_Doc->getSortedStyleList();
|
||
for (int i = 0; i < styleList.count(); ++i)
|
||
{
|
||
const ParagraphStyle& paragraphStyle = m_Doc->paragraphStyles()[styleList[i]];
|
||
- if (names.contains(paragraphStyle.name()))
|
||
+ if (lists.styles().contains(paragraphStyle.name()))
|
||
putPStyle(writer, paragraphStyle, "ParagraphStyle");
|
||
}
|
||
|