1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <config_features.h>
12 #include "formulagroup.hxx"
13 #include "formulagroupcl.hxx"
14 #include "document.hxx"
15 #include "formulacell.hxx"
16 #include "tokenarray.hxx"
17 #include "compiler.hxx"
18 #include "interpre.hxx"
19 #include "scmatrix.hxx"
20 #include "globalnames.hxx"
22 #include <formula/vectortoken.hxx>
23 #include <officecfg/Office/Common.hxx>
24 #if HAVE_FEATURE_OPENCL
25 #include <opencl/platforminfo.hxx>
27 #include <rtl/bootstrap.hxx>
30 #include <unordered_map>
33 #if HAVE_FEATURE_OPENCL
34 #include <opencl/openclwrapper.hxx>
39 FormulaGroupEntry::FormulaGroupEntry( ScFormulaCell
** pCells
, size_t nRow
, size_t nLength
) :
40 mpCells(pCells
), mnRow(nRow
), mnLength(nLength
), mbShared(true) {}
42 FormulaGroupEntry::FormulaGroupEntry( ScFormulaCell
* pCell
, size_t nRow
) :
43 mpCell(pCell
), mnRow(nRow
), mnLength(0), mbShared(false) {}
45 size_t FormulaGroupContext::ColKey::Hash::operator ()( const FormulaGroupContext::ColKey
& rKey
) const
47 return rKey
.mnTab
* MAXCOLCOUNT
+ rKey
.mnCol
;
50 FormulaGroupContext::ColKey::ColKey( SCTAB nTab
, SCCOL nCol
) : mnTab(nTab
), mnCol(nCol
) {}
52 bool FormulaGroupContext::ColKey::operator== ( const ColKey
& r
) const
54 return mnTab
== r
.mnTab
&& mnCol
== r
.mnCol
;
57 bool FormulaGroupContext::ColKey::operator!= ( const ColKey
& r
) const
59 return !operator==(r
);
62 FormulaGroupContext::ColArray::ColArray( NumArrayType
* pNumArray
, StrArrayType
* pStrArray
) :
63 mpNumArray(pNumArray
), mpStrArray(pStrArray
), mnSize(0)
66 mnSize
= mpNumArray
->size();
68 mnSize
= mpStrArray
->size();
71 FormulaGroupContext::ColArray
* FormulaGroupContext::getCachedColArray( SCTAB nTab
, SCCOL nCol
, size_t nSize
)
73 ColArraysType::iterator itColArray
= maColArrays
.find(ColKey(nTab
, nCol
));
74 if (itColArray
== maColArrays
.end())
75 // Not cached for this column.
78 ColArray
& rCached
= itColArray
->second
;
79 if (nSize
> rCached
.mnSize
)
80 // Cached data array is not long enough for the requested range.
86 FormulaGroupContext::ColArray
* FormulaGroupContext::setCachedColArray(
87 SCTAB nTab
, SCCOL nCol
, NumArrayType
* pNumArray
, StrArrayType
* pStrArray
)
89 ColArraysType::iterator it
= maColArrays
.find(ColKey(nTab
, nCol
));
90 if (it
== maColArrays
.end())
92 std::pair
<ColArraysType::iterator
,bool> r
=
94 ColArraysType::value_type(ColKey(nTab
, nCol
), ColArray(pNumArray
, pStrArray
)));
97 // Somehow the insertion failed.
100 return &r
.first
->second
;
103 // Prior array exists for this column. Overwrite it.
104 ColArray
& rArray
= it
->second
;
105 rArray
= ColArray(pNumArray
, pStrArray
);
109 void FormulaGroupContext::ensureStrArray( ColArray
& rColArray
, size_t nArrayLen
)
111 if (rColArray
.mpStrArray
)
114 maStrArrays
.push_back(
115 new sc::FormulaGroupContext::StrArrayType(nArrayLen
, NULL
));
116 rColArray
.mpStrArray
= &maStrArrays
.back();
119 void FormulaGroupContext::ensureNumArray( ColArray
& rColArray
, size_t nArrayLen
)
121 if (rColArray
.mpNumArray
)
125 rtl::math::setNan(&fNan
);
127 maNumArrays
.push_back(
128 new sc::FormulaGroupContext::NumArrayType(nArrayLen
, fNan
));
129 rColArray
.mpNumArray
= &maNumArrays
.back();
132 FormulaGroupContext::FormulaGroupContext()
136 FormulaGroupContext::~FormulaGroupContext()
143 * Input double array consists of segments of NaN's and normal values.
144 * Insert only the normal values into the matrix while skipping the NaN's.
146 void fillMatrix( ScMatrix
& rMat
, size_t nCol
, const double* pNums
, size_t nLen
)
148 const double* pNum
= pNums
;
149 const double* pNumEnd
= pNum
+ nLen
;
150 const double* pNumHead
= NULL
;
151 for (; pNum
!= pNumEnd
; ++pNum
)
153 if (!rtl::math::isNan(*pNum
))
156 // Store the first non-NaN position.
164 // Flush this non-NaN segment to the matrix.
165 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
172 // Flush last non-NaN segment to the matrix.
173 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
177 void flushStrSegment(
178 ScMatrix
& rMat
, size_t nCol
, rtl_uString
** pHead
, rtl_uString
** pCur
, rtl_uString
** pTop
)
180 size_t nOffset
= pHead
- pTop
;
181 std::vector
<svl::SharedString
> aStrs
;
182 aStrs
.reserve(pCur
- pHead
);
183 for (; pHead
!= pCur
; ++pHead
)
184 aStrs
.push_back(svl::SharedString(*pHead
, *pHead
));
186 rMat
.PutString(&aStrs
[0], aStrs
.size(), nCol
, nOffset
);
189 void fillMatrix( ScMatrix
& rMat
, size_t nCol
, rtl_uString
** pStrs
, size_t nLen
)
191 rtl_uString
** p
= pStrs
;
192 rtl_uString
** pEnd
= p
+ nLen
;
193 rtl_uString
** pHead
= NULL
;
194 for (; p
!= pEnd
; ++p
)
199 // Store the first non-empty string position.
207 // Flush this non-empty segment to the matrix.
208 flushStrSegment(rMat
, nCol
, pHead
, p
, pStrs
);
215 // Flush last non-empty segment to the matrix.
216 flushStrSegment(rMat
, nCol
, pHead
, p
, pStrs
);
220 void fillMatrix( ScMatrix
& rMat
, size_t nCol
, const double* pNums
, rtl_uString
** pStrs
, size_t nLen
)
224 fillMatrix(rMat
, nCol
, pNums
, nLen
);
228 const double* pNum
= pNums
;
229 const double* pNumHead
= NULL
;
230 rtl_uString
** pStr
= pStrs
;
231 rtl_uString
** pStrEnd
= pStr
+ nLen
;
232 rtl_uString
** pStrHead
= NULL
;
234 for (; pStr
!= pStrEnd
; ++pStr
, ++pNum
)
238 // String cell exists.
242 // Flush this numeric segment to the matrix.
243 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
248 // Store the first non-empty string position.
254 // No string cell. Check the numeric cell value.
258 // Flush this non-empty string segment to the matrix.
259 flushStrSegment(rMat
, nCol
, pStrHead
, pStr
, pStrs
);
263 if (!rtl::math::isNan(*pNum
))
265 // Numeric cell exists.
267 // Store the first non-NaN position.
273 // Empty cell. No action required.
278 // Flush the last non-empty segment to the matrix.
279 flushStrSegment(rMat
, nCol
, pStrHead
, pStr
, pStrs
);
283 // Flush the last numeric segment to the matrix.
284 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
290 CompiledFormula::CompiledFormula() {}
292 CompiledFormula::~CompiledFormula() {}
294 FormulaGroupInterpreterSoftware::FormulaGroupInterpreterSoftware() : FormulaGroupInterpreter()
298 ScMatrixRef
FormulaGroupInterpreterSoftware::inverseMatrix(const ScMatrix
& /*rMat*/)
300 return ScMatrixRef();
303 CompiledFormula
* FormulaGroupInterpreterSoftware::createCompiledFormula(
304 ScFormulaCellGroup
& /*rGroup*/, ScTokenArray
& /*rCode*/ )
309 bool FormulaGroupInterpreterSoftware::interpret(ScDocument
& rDoc
, const ScAddress
& rTopPos
,
310 ScFormulaCellGroupRef
& xGroup
,
313 typedef std::unordered_map
<const formula::FormulaToken
*, formula::FormulaTokenRef
> CachedTokensType
;
315 // Decompose the group into individual cells and calculate them individually.
317 // The caller must ensure that the top position is the start position of
320 ScAddress aTmpPos
= rTopPos
;
321 std::vector
<formula::FormulaTokenRef
> aResults
;
322 aResults
.reserve(xGroup
->mnLength
);
323 CachedTokensType aCachedTokens
;
326 rtl::math::setNan(&fNan
);
328 for (SCROW i
= 0; i
< xGroup
->mnLength
; ++i
, aTmpPos
.IncRow())
331 for (const formula::FormulaToken
* p
= rCode
.First(); p
; p
= rCode
.Next())
333 CachedTokensType::iterator it
= aCachedTokens
.find(p
);
334 if (it
!= aCachedTokens
.end())
336 // This token is cached. Use the cached one.
337 aCode2
.AddToken(*it
->second
);
341 switch (p
->GetType())
343 case formula::svSingleVectorRef
:
345 const formula::SingleVectorRefToken
* p2
= static_cast<const formula::SingleVectorRefToken
*>(p
);
346 const formula::VectorRefArray
& rArray
= p2
->GetArray();
348 rtl_uString
* pStr
= NULL
;
350 if (static_cast<size_t>(i
) < p2
->GetArrayLength())
352 if (rArray
.mpStringArray
)
353 // See if the cell is of string type.
354 pStr
= rArray
.mpStringArray
[i
];
356 if (!pStr
&& rArray
.mpNumericArray
)
357 fVal
= rArray
.mpNumericArray
[i
];
362 // This is a string cell.
363 svl::SharedStringPool
& rPool
= rDoc
.GetSharedStringPool();
364 aCode2
.AddString(rPool
.intern(OUString(pStr
)));
366 else if (rtl::math::isNan(fVal
))
367 // Value of NaN represents an empty cell.
368 aCode2
.AddToken(ScEmptyCellToken(false, false));
371 aCode2
.AddDouble(fVal
);
374 case formula::svDoubleVectorRef
:
376 const formula::DoubleVectorRefToken
* p2
= static_cast<const formula::DoubleVectorRefToken
*>(p
);
377 const std::vector
<formula::VectorRefArray
>& rArrays
= p2
->GetArrays();
378 size_t nColSize
= rArrays
.size();
379 size_t nRowStart
= p2
->IsStartFixed() ? 0 : i
;
380 size_t nRowEnd
= p2
->GetRefRowSize() - 1;
381 if (!p2
->IsEndFixed())
383 size_t nRowSize
= nRowEnd
- nRowStart
+ 1;
384 ScMatrixRef
pMat(new ScMatrix(nColSize
, nRowSize
));
386 size_t nDataRowEnd
= p2
->GetArrayLength() - 1;
387 if (nRowStart
> nDataRowEnd
)
388 // Referenced rows are all empty.
390 else if (nRowEnd
> nDataRowEnd
)
391 // Data array is shorter than the row size of the reference. Truncate it to the data.
392 nRowSize
-= nRowEnd
- nDataRowEnd
;
394 for (size_t nCol
= 0; nCol
< nColSize
; ++nCol
)
396 const formula::VectorRefArray
& rArray
= rArrays
[nCol
];
397 if (rArray
.mpStringArray
)
399 if (rArray
.mpNumericArray
)
401 // Mixture of string and numeric values.
402 const double* pNums
= rArray
.mpNumericArray
;
404 rtl_uString
** pStrs
= rArray
.mpStringArray
;
406 fillMatrix(*pMat
, nCol
, pNums
, pStrs
, nRowSize
);
410 // String cells only.
411 rtl_uString
** pStrs
= rArray
.mpStringArray
;
413 fillMatrix(*pMat
, nCol
, pStrs
, nRowSize
);
416 else if (rArray
.mpNumericArray
)
418 // Numeric cells only.
419 const double* pNums
= rArray
.mpNumericArray
;
421 fillMatrix(*pMat
, nCol
, pNums
, nRowSize
);
425 if (p2
->IsStartFixed() && p2
->IsEndFixed())
427 // Cached the converted token for absolute range referene.
428 ScComplexRefData aRef
;
429 ScRange aRefRange
= rTopPos
;
430 aRefRange
.aEnd
.SetRow(rTopPos
.Row() + nRowEnd
);
431 aRef
.InitRange(aRefRange
);
432 formula::FormulaTokenRef
xTok(new ScMatrixRangeToken(pMat
, aRef
));
433 aCachedTokens
.insert(CachedTokensType::value_type(p
, xTok
));
434 aCode2
.AddToken(*xTok
);
438 ScMatrixToken
aTok(pMat
);
439 aCode2
.AddToken(aTok
);
448 ScFormulaCell
* pDest
= rDoc
.GetFormulaCell(aTmpPos
);
452 ScCompiler
aComp(&rDoc
, aTmpPos
, aCode2
);
453 aComp
.CompileTokenArray();
454 ScInterpreter
aInterpreter(pDest
, &rDoc
, aTmpPos
, aCode2
);
455 aInterpreter
.Interpret();
456 aResults
.push_back(aInterpreter
.GetResultToken());
457 } // for loop end (xGroup->mnLength)
459 if (!aResults
.empty())
460 rDoc
.SetFormulaResults(rTopPos
, &aResults
[0], aResults
.size());
465 FormulaGroupInterpreter
*FormulaGroupInterpreter::msInstance
= NULL
;
467 void FormulaGroupInterpreter::MergeCalcConfig(const ScDocument
& rDoc
)
469 maCalcConfig
= ScInterpreter::GetGlobalConfig();
470 maCalcConfig
.MergeDocumentSpecific(rDoc
.GetCalcConfig());
473 /// load and/or configure the correct formula group interpreter
474 FormulaGroupInterpreter
*FormulaGroupInterpreter::getStatic()
478 #if HAVE_FEATURE_OPENCL
479 const ScCalcConfig
& rConfig
= ScInterpreter::GetGlobalConfig();
480 if (officecfg::Office::Common::Misc::UseOpenCL::get())
481 switchOpenCLDevice(rConfig
.maOpenCLDevice
, rConfig
.mbOpenCLAutoSelect
, false);
483 static bool bAllowSoftwareInterpreter
= (getenv("SC_ALLOW_BROKEN_SOFTWARE_INTERPRETER") != NULL
);
485 if ( !msInstance
&& bAllowSoftwareInterpreter
) // software fallback
487 SAL_INFO("sc.formulagroup", "Create S/W interpreter");
488 msInstance
= new sc::FormulaGroupInterpreterSoftware();
495 #if HAVE_FEATURE_OPENCL
496 void FormulaGroupInterpreter::fillOpenCLInfo(std::vector
<OpenCLPlatformInfo
>& rPlatforms
)
498 const std::vector
<OpenCLPlatformInfo
>& rPlatformsFromWrapper
=
499 ::opencl::fillOpenCLInfo();
501 rPlatforms
.assign(rPlatformsFromWrapper
.begin(), rPlatformsFromWrapper
.end());
504 bool FormulaGroupInterpreter::switchOpenCLDevice(const OUString
& rDeviceId
, bool bAutoSelect
, bool bForceEvaluation
)
506 bool bOpenCLEnabled
= officecfg::Office::Common::Misc::UseOpenCL::get();
507 static bool bAllowSoftwareInterpreter
= (getenv("SC_ALLOW_BROKEN_SOFTWARE_INTERPRETER") != NULL
);
508 if (!bOpenCLEnabled
|| (bAllowSoftwareInterpreter
&& rDeviceId
== OPENCL_SOFTWARE_DEVICE_CONFIG_NAME
))
512 // if we already have a software interpreter don't delete it
513 if(dynamic_cast<sc::FormulaGroupInterpreterSoftware
*>(msInstance
))
519 msInstance
= new sc::FormulaGroupInterpreterSoftware();
522 bool bSuccess
= ::opencl::switchOpenCLDevice(&rDeviceId
, bAutoSelect
, bForceEvaluation
);
529 if ( officecfg::Office::Common::Misc::UseOpenCL::get() )
531 msInstance
= new sc::opencl::FormulaGroupInterpreterOpenCL();
532 return msInstance
!= NULL
;
538 void FormulaGroupInterpreter::getOpenCLDeviceInfo(sal_Int32
& rDeviceId
, sal_Int32
& rPlatformId
)
542 bool bOpenCLEnabled
= officecfg::Office::Common::Misc::UseOpenCL::get();
546 size_t aDeviceId
= static_cast<size_t>(-1);
547 size_t aPlatformId
= static_cast<size_t>(-1);
549 ::opencl::getOpenCLDeviceInfo(aDeviceId
, aPlatformId
);
550 rDeviceId
= aDeviceId
;
551 rPlatformId
= aPlatformId
;
554 void FormulaGroupInterpreter::enableOpenCL_UnitTestsOnly()
556 std::shared_ptr
<comphelper::ConfigurationChanges
> batch(comphelper::ConfigurationChanges::create());
557 officecfg::Office::Common::Misc::UseOpenCL::set(true, batch
);
560 ScCalcConfig aConfig
= ScInterpreter::GetGlobalConfig();
562 aConfig
.mbOpenCLSubsetOnly
= false;
563 aConfig
.mnOpenCLMinimumFormulaGroupSize
= 2;
565 ScInterpreter::SetGlobalConfig(aConfig
);
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */