java-topology/defects/openoffice-0001/patch/openoffice-0001.patch

132 lines
5.9 KiB
Diff
Raw Permalink 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-000001136
# openoffice-0001: XclExpXFBuffer::AddBorderAndFill O(N²) std::find_if on maBorders/maFills
#
# In main/sc/source/filter/excel/xestyle.cxx, AddBorderAndFill() is called once
# per XF record during .xlsx export (via AppendXFIndex). Each call performs two
# linear scans: one over maBorders (XclExpCellBorder vector) and one over
# maFills (XclExpCellArea vector). With N unique XF styles the total work is
# O(N²): N calls × O(N) scan each.
#
# SaveXFXml() also does two find_if passes per XF to locate the index — same
# O(N) per XF, so O(N²) total over all XF records being saved.
#
# A large spreadsheet with many unique cell styles (borders + fills) triggers
# this path heavily. EXC_XF_MAXCOUNT caps at 4050, giving up to ~8M comparisons
# for the border list and ~8M for fills in the worst case — 16M total where 4050
# O(1) lookups would suffice.
#
# Fix: add parallel unordered_map<> index tables (maBorderIndex, maFillIndex)
# that map a packed key derived from each struct's fields to the insertion index.
# AddBorderAndFill does a hash lookup before push_back; SaveXFXml reads the index
# directly instead of scanning.
#
# Severity: MEDIUM-HIGH — O(N²) on .xlsx export with diverse cell styles;
# EXC_XF_MAXCOUNT = 4050 so worst case ~16M field comparisons → ~O(1) with map.
# Ratio: ~4050x at the maximum style count.
--- a/main/sc/source/filter/excel/xestyle.cxx
+++ b/main/sc/source/filter/excel/xestyle.cxx
@@ -2360,6 +2360,20 @@
maBorders(),
maFills(),
+ maBorderIndex(),
+ maFillIndex(),
maXclExpXFMap()
{
}
+// Pack XclExpCellBorder fields into a uint64_t key for O(1) lookup.
+static sal_uInt64 lcl_BorderKey( const XclExpCellBorder& r )
+{
+ return (sal_uInt64(r.mnLeftColor) << 48)
+ ^ (sal_uInt64(r.mnRightColor) << 40)
+ ^ (sal_uInt64(r.mnTopColor) << 32)
+ ^ (sal_uInt64(r.mnBottomColor) << 24)
+ ^ (sal_uInt64(r.mnDiagColor) << 16)
+ ^ (sal_uInt64(r.mnLeftLine) << 12)
+ ^ (sal_uInt64(r.mnRightLine) << 8)
+ ^ (sal_uInt64(r.mnTopLine) << 4)
+ ^ (sal_uInt64(r.mnBottomLine))
+ ^ (sal_uInt64(r.mbDiagTLtoBR) << 60)
+ ^ (sal_uInt64(r.mbDiagBLtoTR) << 61)
+ ^ (sal_uInt64(r.mnLeftColorId) * 0x9e3779b9ULL)
+ ^ (sal_uInt64(r.mnRightColorId) * 0x6c62272eULL)
+ ^ (sal_uInt64(r.mnTopColorId) * 0x517cc1b7ULL)
+ ^ (sal_uInt64(r.mnBottomColorId) * 0x27d4eb2fULL)
+ ^ (sal_uInt64(r.mnDiagColorId) * 0xb492b66fULL);
+}
+
+// Pack XclExpCellArea fields into a uint64_t key for O(1) lookup.
+static sal_uInt64 lcl_FillKey( const XclExpCellArea& r )
+{
+ return (sal_uInt64(r.mnForeColor) << 48)
+ ^ (sal_uInt64(r.mnBackColor) << 32)
+ ^ (sal_uInt64(r.mnPattern) << 16)
+ ^ (sal_uInt64(r.mnForeColorId) * 0x9e3779b9ULL)
+ ^ (sal_uInt64(r.mnBackColorId) * 0x6c62272eULL);
+}
+
void XclExpXFBuffer::AddBorderAndFill( const XclExpXF& rXF )
{
- if( std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) ) == maBorders.end() )
- {
+ sal_uInt64 nBorderKey = lcl_BorderKey( rXF.GetBorderData() );
+ if( maBorderIndex.find( nBorderKey ) == maBorderIndex.end() )
+ {
+ maBorderIndex[ nBorderKey ] = static_cast<sal_uInt32>( maBorders.size() );
maBorders.push_back( rXF.GetBorderData() );
}
- if( std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) ) == maFills.end() )
- {
+ sal_uInt64 nFillKey = lcl_FillKey( rXF.GetAreaData() );
+ if( maFillIndex.find( nFillKey ) == maFillIndex.end() )
+ {
+ maFillIndex[ nFillKey ] = static_cast<sal_uInt32>( maFills.size() );
maFills.push_back( rXF.GetAreaData() );
}
}
void XclExpXFBuffer::SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF )
{
- XclExpBorderList::iterator aBorderPos =
- std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) );
- DBG_ASSERT( aBorderPos != maBorders.end(), "XclExpXFBuffer::SaveXml - Invalid @borderId!" );
- XclExpFillList::iterator aFillPos =
- std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) );
- DBG_ASSERT( aFillPos != maFills.end(), "XclExpXFBuffer::SaveXml - Invalid @fillId!" );
-
- sal_Int32 nBorderId = 0, nFillId = 0;
- if( aBorderPos != maBorders.end() )
- nBorderId = std::distance( maBorders.begin(), aBorderPos );
- if( aFillPos != maFills.end() )
- nFillId = std::distance( maFills.begin(), aFillPos );
+ sal_Int32 nBorderId = 0, nFillId = 0;
+ {
+ auto it = maBorderIndex.find( lcl_BorderKey( rXF.GetBorderData() ) );
+ DBG_ASSERT( it != maBorderIndex.end(), "XclExpXFBuffer::SaveXml - Invalid @borderId!" );
+ if( it != maBorderIndex.end() )
+ nBorderId = static_cast<sal_Int32>( it->second );
+ }
+ {
+ auto it = maFillIndex.find( lcl_FillKey( rXF.GetAreaData() ) );
+ DBG_ASSERT( it != maFillIndex.end(), "XclExpXFBuffer::SaveXml - Invalid @fillId!" );
+ if( it != maFillIndex.end() )
+ nFillId = static_cast<sal_Int32>( it->second );
+ }
rXF.SetXmlIds( nBorderId, nFillId );
rXF.SaveXml( rStrm );
}
--- a/main/sc/source/filter/inc/xestyle.hxx
+++ b/main/sc/source/filter/inc/xestyle.hxx
@@ -757,6 +757,8 @@
+ #include <unordered_map>
+
typedef ::std::vector< XclExpCellBorder > XclExpBorderList;
typedef ::std::vector< XclExpCellArea > XclExpFillList;
+ typedef ::std::unordered_map< sal_uInt64, sal_uInt32 > XclExpIndexMap;
XclExpBorderList maBorders; /// List of borders used by XF records
XclExpFillList maFills; /// List of fills used by XF records
+ XclExpIndexMap maBorderIndex; /// Hash map: border key → index in maBorders
+ XclExpIndexMap maFillIndex; /// Hash map: fill key → index in maFills