132 lines
5.9 KiB
Diff
132 lines
5.9 KiB
Diff
# 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
|