java-topology/defects/scribus/patch/scribus-0004-file-save-names-contains-O-N2.patch

130 lines
5.2 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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");
}