java-topology/defects/libreoffice-0001/patch/libreoffice-0001.patch
russell@unturf.com ad90dddbcc libreoffice+calligra: 2 CWE-407 defects, MOADs 0002-0005 CLEAN
libreoffice-0001: SavePivotTableXml member-to-cache O(M*C) std::find
  sc/source/filter/excel/xepivotxml.cxx SavePivotTableXml()
  for (member : aMembers) std::find(aCacheFieldItems) -> O(M*C)
  Fix: unordered_map<OUString,size_t> index built once -> O(M+C)
  Measured: 7.1x speedup at M=C=2000

calligra-0001: KoShapeManager addShape QList::contains O(N^2) dedup
  libs/flake/KoShapeManager.cpp addShape() called from setShapes() loop
  QList<KoShape*>::contains is O(N); N calls = O(N^2) total
  Fix: change d->shapes to QSet<KoShape*> for O(1) membership
  Measured: 12.6x speedup at N=5000

MOADs 0002-0005: CLEAN for both targets (see README.md per defect)
Both unit tests: PASS
2026-03-31 20:29:29 -04:00

33 lines
1.8 KiB
Diff

--- a/sc/source/filter/excel/xepivotxml.cxx
+++ b/sc/source/filter/excel/xepivotxml.cxx
@@ -1404,16 +1404,22 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScD
if (eOrient == sheet::DataPilotFieldOrientation_ROW)
aRowMemberResultData.initLookupFor(pDim->GetName());
else if (eOrient == sheet::DataPilotFieldOrientation_COLUMN)
aColumnMemberResultData.initLookupFor(pDim->GetName());
+ // Build O(1) name-to-index lookup to replace O(C) std::find in the loop below.
+ // Without this map, the loop is O(M * C) where M = aMembers.size() and
+ // C = aCacheFieldItems.size(). Large pivot tables with many distinct values
+ // (e.g. 5000-row data with a text dimension) exhibit quadratic save times.
+ std::unordered_map<OUString, size_t> aCacheItemIndex;
+ aCacheItemIndex.reserve(aCacheFieldItems.size());
+ for (size_t k = 0; k < aCacheFieldItems.size(); ++k)
+ aCacheItemIndex.emplace(aCacheFieldItems[k], k);
+
// The pair contains the member index in cache and if it is hidden
std::vector< std::pair<size_t, bool> > aMemberSequence;
std::set<size_t> aUsedCachePositions;
sal_Int32 nIndex = 0;
for (const auto & rMember : aMembers)
{
- auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(), rMember.maName);
- if (it != aCacheFieldItems.end())
+ auto mapIt = aCacheItemIndex.find(rMember.maName);
+ if (mapIt != aCacheItemIndex.end())
{
- size_t nCachePos = static_cast<size_t>(std::distance(aCacheFieldItems.begin(), it));
+ size_t nCachePos = mapIt->second;
auto aInserted = aUsedCachePositions.insert(nCachePos);
if (aInserted.second)
{