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 "document.hxx"
14 #include "formulacell.hxx"
15 #include "tokenarray.hxx"
16 #include "compiler.hxx"
17 #include "interpre.hxx"
18 #include "scmatrix.hxx"
20 #include "formula/vectortoken.hxx"
21 #include "rtl/bootstrap.hxx"
24 #include <boost/unordered_map.hpp>
26 #define USE_DUMMY_INTERPRETER 0
30 #ifdef DISABLE_DYNLOADING
32 extern "C" size_t getOpenCLPlatformCount(void);
33 extern "C" void fillOpenCLInfo(sc::OpenclPlatformInfo
*, size_t);
34 extern "C" bool switchOpenClDevice(const OUString
*, bool, bool);
35 extern "C" sc::FormulaGroupInterpreter
* createFormulaGroupOpenCLInterpreter();
36 extern "C" void getOpenCLDeviceInfo(size_t*, size_t*);
42 size_t FormulaGroupContext::ColKey::Hash::operator ()( const FormulaGroupContext::ColKey
& rKey
) const
44 return rKey
.mnTab
* MAXCOLCOUNT
+ rKey
.mnCol
;
47 FormulaGroupContext::ColKey::ColKey( SCTAB nTab
, SCCOL nCol
) : mnTab(nTab
), mnCol(nCol
) {}
49 bool FormulaGroupContext::ColKey::operator== ( const ColKey
& r
) const
51 return mnTab
== r
.mnTab
&& mnCol
== r
.mnCol
;
54 bool FormulaGroupContext::ColKey::operator!= ( const ColKey
& r
) const
56 return !operator==(r
);
59 FormulaGroupContext::ColArray::ColArray( NumArrayType
* pNumArray
, StrArrayType
* pStrArray
) :
60 mpNumArray(pNumArray
), mpStrArray(pStrArray
), mnSize(0)
63 mnSize
= mpNumArray
->size();
65 mnSize
= mpStrArray
->size();
68 FormulaGroupContext::ColArray
* FormulaGroupContext::getCachedColArray( SCTAB nTab
, SCCOL nCol
, size_t nSize
)
70 ColArraysType::iterator itColArray
= maColArrays
.find(ColKey(nTab
, nCol
));
71 if (itColArray
== maColArrays
.end())
72 // Not cached for this column.
75 ColArray
& rCached
= itColArray
->second
;
76 if (nSize
> rCached
.mnSize
)
77 // Cached data array is not long enough for the requested range.
83 FormulaGroupContext::ColArray
* FormulaGroupContext::setCachedColArray(
84 SCTAB nTab
, SCCOL nCol
, NumArrayType
* pNumArray
, StrArrayType
* pStrArray
)
86 ColArraysType::iterator it
= maColArrays
.find(ColKey(nTab
, nCol
));
87 if (it
== maColArrays
.end())
89 std::pair
<ColArraysType::iterator
,bool> r
=
91 ColArraysType::value_type(ColKey(nTab
, nCol
), ColArray(pNumArray
, pStrArray
)));
94 // Somehow the insertion failed.
97 return &r
.first
->second
;
100 // Prior array exists for this column. Overwrite it.
101 ColArray
& rArray
= it
->second
;
102 rArray
= ColArray(pNumArray
, pStrArray
);
106 void FormulaGroupContext::ensureStrArray( ColArray
& rColArray
, size_t nArrayLen
)
108 if (rColArray
.mpStrArray
)
111 maStrArrays
.push_back(
112 new sc::FormulaGroupContext::StrArrayType(nArrayLen
, NULL
));
113 rColArray
.mpStrArray
= &maStrArrays
.back();
116 void FormulaGroupContext::ensureNumArray( ColArray
& rColArray
, size_t nArrayLen
)
118 if (rColArray
.mpNumArray
)
122 rtl::math::setNan(&fNan
);
124 maNumArrays
.push_back(
125 new sc::FormulaGroupContext::NumArrayType(nArrayLen
, fNan
));
126 rColArray
.mpNumArray
= &maNumArrays
.back();
132 * Input double array consists of segments of NaN's and normal values.
133 * Insert only the normal values into the matrix while skipping the NaN's.
135 void fillMatrix( ScMatrix
& rMat
, size_t nCol
, const double* pNums
, size_t nLen
)
137 const double* pNum
= pNums
;
138 const double* pNumEnd
= pNum
+ nLen
;
139 const double* pNumHead
= NULL
;
140 for (; pNum
!= pNumEnd
; ++pNum
)
142 if (!rtl::math::isNan(*pNum
))
145 // Store the first non-NaN position.
153 // Flush this non-NaN segment to the matrix.
154 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
161 // Flush last non-NaN segment to the matrix.
162 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
166 void flushStrSegment(
167 ScMatrix
& rMat
, size_t nCol
, rtl_uString
** pHead
, rtl_uString
** pCur
, rtl_uString
** pTop
)
169 size_t nOffset
= pHead
- pTop
;
170 std::vector
<svl::SharedString
> aStrs
;
171 aStrs
.reserve(pCur
- pHead
);
172 for (; pHead
!= pCur
; ++pHead
)
173 aStrs
.push_back(svl::SharedString(*pHead
, *pHead
));
175 rMat
.PutString(&aStrs
[0], aStrs
.size(), nCol
, nOffset
);
178 void fillMatrix( ScMatrix
& rMat
, size_t nCol
, rtl_uString
** pStrs
, size_t nLen
)
180 rtl_uString
** p
= pStrs
;
181 rtl_uString
** pEnd
= p
+ nLen
;
182 rtl_uString
** pHead
= NULL
;
183 for (; p
!= pEnd
; ++p
)
188 // Store the first non-empty string position.
196 // Flush this non-empty segment to the matrix.
197 flushStrSegment(rMat
, nCol
, pHead
, p
, pStrs
);
204 // Flush last non-empty segment to the matrix.
205 flushStrSegment(rMat
, nCol
, pHead
, p
, pStrs
);
209 void fillMatrix( ScMatrix
& rMat
, size_t nCol
, const double* pNums
, rtl_uString
** pStrs
, size_t nLen
)
213 fillMatrix(rMat
, nCol
, pNums
, nLen
);
217 const double* pNum
= pNums
;
218 const double* pNumHead
= NULL
;
219 rtl_uString
** pStr
= pStrs
;
220 rtl_uString
** pStrEnd
= pStr
+ nLen
;
221 rtl_uString
** pStrHead
= NULL
;
223 for (; pStr
!= pStrEnd
; ++pStr
, ++pNum
)
227 // String cell exists.
231 // Flush this numeric segment to the matrix.
232 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
237 // Store the first non-empty string position.
243 // No string cell. Check the numeric cell value.
247 // Flush this non-empty string segment to the matrix.
248 flushStrSegment(rMat
, nCol
, pStrHead
, pStr
, pStrs
);
252 if (!rtl::math::isNan(*pNum
))
254 // Numeric cell exists.
256 // Store the first non-NaN position.
262 // Empty cell. No action required.
267 // Flush the last non-empty segment to the matrix.
268 flushStrSegment(rMat
, nCol
, pStrHead
, pStr
, pStrs
);
272 // Flush the last numeric segment to the matrix.
273 rMat
.PutDouble(pNumHead
, pNum
- pNumHead
, nCol
, pNumHead
- pNums
);
279 FormulaGroupInterpreterSoftware::FormulaGroupInterpreterSoftware() : FormulaGroupInterpreter()
283 ScMatrixRef
FormulaGroupInterpreterSoftware::inverseMatrix(const ScMatrix
& /*rMat*/)
285 return ScMatrixRef();
288 CompiledFormula
* FormulaGroupInterpreterSoftware::createCompiledFormula(ScDocument
& /* rDoc */,
289 const ScAddress
& /* rTopPos */,
290 ScFormulaCellGroupRef
& /* xGroup */,
291 ScTokenArray
& /* rCode */)
296 bool FormulaGroupInterpreterSoftware::interpret(ScDocument
& rDoc
, const ScAddress
& rTopPos
,
297 ScFormulaCellGroupRef
& xGroup
,
300 typedef boost::unordered_map
<const formula::FormulaToken
*, formula::FormulaTokenRef
> CachedTokensType
;
302 // Decompose the group into individual cells and calculate them individually.
304 // The caller must ensure that the top position is the start position of
307 ScAddress aTmpPos
= rTopPos
;
308 std::vector
<formula::FormulaTokenRef
> aResults
;
309 aResults
.reserve(xGroup
->mnLength
);
310 CachedTokensType aCachedTokens
;
313 rtl::math::setNan(&fNan
);
315 for (SCROW i
= 0; i
< xGroup
->mnLength
; ++i
, aTmpPos
.IncRow())
318 for (const formula::FormulaToken
* p
= rCode
.First(); p
; p
= rCode
.Next())
320 CachedTokensType::iterator it
= aCachedTokens
.find(p
);
321 if (it
!= aCachedTokens
.end())
323 // This token is cached. Use the cached one.
324 aCode2
.AddToken(*it
->second
);
328 switch (p
->GetType())
330 case formula::svSingleVectorRef
:
332 const formula::SingleVectorRefToken
* p2
= static_cast<const formula::SingleVectorRefToken
*>(p
);
333 const formula::VectorRefArray
& rArray
= p2
->GetArray();
335 rtl_uString
* pStr
= NULL
;
337 if (static_cast<size_t>(i
) < p2
->GetArrayLength())
339 if (rArray
.mpStringArray
)
340 // See if the cell is of string type.
341 pStr
= rArray
.mpStringArray
[i
];
343 if (!pStr
&& rArray
.mpNumericArray
)
344 fVal
= rArray
.mpNumericArray
[i
];
348 // This is a string cell.
349 aCode2
.AddString(OUString(pStr
));
350 else if (rtl::math::isNan(fVal
))
351 // Value of NaN represents an empty cell.
352 aCode2
.AddToken(ScEmptyCellToken(false, false));
355 aCode2
.AddDouble(fVal
);
358 case formula::svDoubleVectorRef
:
360 const formula::DoubleVectorRefToken
* p2
= static_cast<const formula::DoubleVectorRefToken
*>(p
);
361 const std::vector
<formula::VectorRefArray
>& rArrays
= p2
->GetArrays();
362 size_t nColSize
= rArrays
.size();
363 size_t nRowStart
= p2
->IsStartFixed() ? 0 : i
;
364 size_t nRowEnd
= p2
->GetRefRowSize() - 1;
365 if (!p2
->IsEndFixed())
367 size_t nRowSize
= nRowEnd
- nRowStart
+ 1;
368 ScMatrixRef
pMat(new ScMatrix(nColSize
, nRowSize
));
370 size_t nDataRowEnd
= p2
->GetArrayLength() - 1;
371 if (nRowStart
> nDataRowEnd
)
372 // Referenced rows are all empty.
374 else if (nRowEnd
> nDataRowEnd
)
375 // Data array is shorter than the row size of the reference. Truncate it to the data.
376 nRowSize
-= nRowEnd
- nDataRowEnd
;
378 for (size_t nCol
= 0; nCol
< nColSize
; ++nCol
)
380 const formula::VectorRefArray
& rArray
= rArrays
[nCol
];
381 if (rArray
.mpStringArray
)
383 if (rArray
.mpNumericArray
)
385 // Mixture of string and numeric values.
386 const double* pNums
= rArray
.mpNumericArray
;
388 rtl_uString
** pStrs
= rArray
.mpStringArray
;
390 fillMatrix(*pMat
, nCol
, pNums
, pStrs
, nRowSize
);
394 // String cells only.
395 rtl_uString
** pStrs
= rArray
.mpStringArray
;
397 fillMatrix(*pMat
, nCol
, pStrs
, nRowSize
);
400 else if (rArray
.mpNumericArray
)
402 // Numeric cells only.
403 const double* pNums
= rArray
.mpNumericArray
;
405 fillMatrix(*pMat
, nCol
, pNums
, nRowSize
);
409 if (p2
->IsStartFixed() && p2
->IsEndFixed())
411 // Cached the converted token for absolute range referene.
412 ScComplexRefData aRef
;
413 ScRange aRefRange
= rTopPos
;
414 aRefRange
.aEnd
.SetRow(rTopPos
.Row() + nRowEnd
);
415 aRef
.InitRange(aRefRange
);
416 formula::FormulaTokenRef
xTok(new ScMatrixRangeToken(pMat
, aRef
));
417 aCachedTokens
.insert(CachedTokensType::value_type(p
, xTok
));
418 aCode2
.AddToken(*xTok
);
422 ScMatrixToken
aTok(pMat
);
423 aCode2
.AddToken(aTok
);
432 ScFormulaCell
* pDest
= rDoc
.GetFormulaCell(aTmpPos
);
436 ScCompiler
aComp(&rDoc
, aTmpPos
, aCode2
);
437 aComp
.CompileTokenArray();
438 ScInterpreter
aInterpreter(pDest
, &rDoc
, aTmpPos
, aCode2
);
439 aInterpreter
.Interpret();
440 aResults
.push_back(aInterpreter
.GetResultToken());
441 } // for loop end (xGroup->mnLength)
443 if (!aResults
.empty())
444 rDoc
.SetFormulaResults(rTopPos
, &aResults
[0], aResults
.size());
449 #if USE_DUMMY_INTERPRETER
450 class FormulaGroupInterpreterDummy
: public FormulaGroupInterpreter
457 FormulaGroupInterpreterDummy()
459 const char *pValue
= getenv("FORMULA_GROUP_DUMMY");
460 meMode
= static_cast<Mode
>(OString(pValue
, strlen(pValue
)).toInt32());
461 fprintf(stderr
, "Using Dummy Formula Group interpreter mode %d\n", (int)meMode
);
464 virtual ScMatrixRef
inverseMatrix(const ScMatrix
& /*rMat*/)
466 return ScMatrixRef();
469 virtual bool interpret(ScDocument
& rDoc
, const ScAddress
& rTopPos
,
470 const ScFormulaCellGroupRef
& xGroup
,
475 // Write simple data back into the sheet
476 if (meMode
== WRITE_OUTPUT
)
478 double *pDoubles
= new double[xGroup
->mnLength
];
479 for (sal_Int32 i
= 0; i
< xGroup
->mnLength
; i
++)
480 pDoubles
[i
] = 42.0 + i
;
481 rDoc
.SetFormulaResults(rTopPos
, pDoubles
, xGroup
->mnLength
);
490 #ifndef DISABLE_DYNLOADING
492 class FormulaGroupInterpreterOpenCLMissing
: public FormulaGroupInterpreter
495 FormulaGroupInterpreterOpenCLMissing() : FormulaGroupInterpreter() {}
496 virtual ~FormulaGroupInterpreterOpenCLMissing() {}
497 virtual ScMatrixRef
inverseMatrix(const ScMatrix
&) { return ScMatrixRef(); }
498 virtual CompiledFormula
* createCompiledFormula(ScDocument
&, const ScAddress
&, ScFormulaCellGroupRef
&, ScTokenArray
&) SAL_OVERRIDE
{ return NULL
; }
499 virtual bool interpret(ScDocument
&, const ScAddress
&, ScFormulaCellGroupRef
&, ScTokenArray
&) { return false; }
502 static void SAL_CALL
thisModule() {}
504 typedef FormulaGroupInterpreter
* (*__createFormulaGroupOpenCLInterpreter
)(void);
505 typedef size_t (*__getOpenCLPlatformCount
)(void);
506 typedef void (*__fillOpenCLInfo
)(OpenclPlatformInfo
*, size_t);
507 typedef bool (*__switchOpenClDevice
)(const OUString
*, bool, bool);
508 typedef void (*__getOpenCLDeviceInfo
)(size_t*, size_t*);
512 FormulaGroupInterpreter
*FormulaGroupInterpreter::msInstance
= NULL
;
514 #ifndef DISABLE_DYNLOADING
516 osl::Module
* getOpenCLModule()
518 static osl::Module aModule
;
523 OUString
aLibName(SVLIBRARY("scopencl"));
524 bool bLoaded
= aModule
.loadRelative(&thisModule
, aLibName
);
526 bLoaded
= aModule
.load(aLibName
);
528 return bLoaded
? &aModule
: NULL
;
533 /// load and/or configure the correct formula group interpreter
534 FormulaGroupInterpreter
*FormulaGroupInterpreter::getStatic()
536 #if USE_DUMMY_INTERPRETER
537 if (getenv("FORMULA_GROUP_DUMMY"))
540 return msInstance
= new sc::FormulaGroupInterpreterDummy();
546 const ScCalcConfig
& rConfig
= ScInterpreter::GetGlobalConfig();
547 if (rConfig
.mbOpenCLEnabled
)
548 switchOpenCLDevice(rConfig
.maOpenCLDevice
, rConfig
.mbOpenCLAutoSelect
, false);
550 if ( !msInstance
) // software fallback
552 fprintf(stderr
, "Create S/W interp\n");
553 msInstance
= new sc::FormulaGroupInterpreterSoftware();
560 void FormulaGroupInterpreter::fillOpenCLInfo(std::vector
<OpenclPlatformInfo
>& rPlatforms
)
562 #ifndef DISABLE_DYNLOADING
563 osl::Module
* pModule
= getOpenCLModule();
567 oslGenericFunction fn
= pModule
->getFunctionSymbol("getOpenCLPlatformCount");
571 size_t nPlatforms
= reinterpret_cast<__getOpenCLPlatformCount
>(fn
)();
575 fn
= pModule
->getFunctionSymbol("fillOpenCLInfo");
579 std::vector
<OpenclPlatformInfo
> aPlatforms(nPlatforms
);
580 reinterpret_cast<__fillOpenCLInfo
>(fn
)(&aPlatforms
[0], aPlatforms
.size());
581 rPlatforms
.swap(aPlatforms
);
583 size_t nPlatforms
= getOpenCLPlatformCount();
587 std::vector
<OpenclPlatformInfo
> aPlatforms(nPlatforms
);
588 ::fillOpenCLInfo(&aPlatforms
[0], aPlatforms
.size());
589 rPlatforms
.swap(aPlatforms
);
593 bool FormulaGroupInterpreter::switchOpenCLDevice(const OUString
& rDeviceId
, bool bAutoSelect
, bool bForceEvaluation
)
595 bool bOpenCLEnabled
= ScInterpreter::GetGlobalConfig().mbOpenCLEnabled
;
596 if(!bOpenCLEnabled
|| rDeviceId
== "Software")
600 // if we already have a software interpreter don't delete it
601 if(dynamic_cast<sc::FormulaGroupInterpreterSoftware
*>(msInstance
))
607 msInstance
= new sc::FormulaGroupInterpreterSoftware();
610 #if HAVE_FEATURE_OPENCL
611 #ifndef DISABLE_DYNLOADING
612 osl::Module
* pModule
= getOpenCLModule();
616 oslGenericFunction fn
= pModule
->getFunctionSymbol("switchOpenClDevice");
620 bool bSuccess
= reinterpret_cast<__switchOpenClDevice
>(fn
)(&rDeviceId
, bAutoSelect
, bForceEvaluation
);
624 bool bSuccess
= switchOpenClDevice(&rDeviceId
, bAutoSelect
, bForceEvaluation
);
635 #if HAVE_FEATURE_OPENCL
636 if ( ScInterpreter::GetGlobalConfig().mbOpenCLEnabled
)
638 #ifdef DISABLE_DYNLOADING
639 msInstance
= createFormulaGroupOpenCLInterpreter();
640 return msInstance
!= NULL
;
642 // Dynamically load scopencl shared object, and instantiate the opencl interpreter.
644 fn
= pModule
->getFunctionSymbol("createFormulaGroupOpenCLInterpreter");
647 msInstance
= reinterpret_cast<__createFormulaGroupOpenCLInterpreter
>(fn
)();
648 bSuccess
= msInstance
!= NULL
;
652 msInstance
= new sc::FormulaGroupInterpreterOpenCLMissing();
658 (void) bForceEvaluation
;
663 void FormulaGroupInterpreter::getOpenCLDeviceInfo(sal_Int32
& rDeviceId
, sal_Int32
& rPlatformId
)
667 bool bOpenCLEnabled
= ScInterpreter::GetGlobalConfig().mbOpenCLEnabled
;
671 #if HAVE_FEATURE_OPENCL
673 size_t aDeviceId
= -1;
674 size_t aPlatformId
= -1;
676 #ifndef DISABLE_DYNLOADING
677 osl::Module
* pModule
= getOpenCLModule();
681 oslGenericFunction fn
= pModule
->getFunctionSymbol("getOpenCLDeviceInfo");
685 reinterpret_cast<__getOpenCLDeviceInfo
>(fn
)(&aDeviceId
, &aPlatformId
);
687 getOpenCLDeviceInfo(&aDeviceId
, &aPlatformId
);
689 rDeviceId
= aDeviceId
;
690 rPlatformId
= aPlatformId
;
694 void FormulaGroupInterpreter::enableOpenCL(bool bEnable
)
696 ScCalcConfig aConfig
= ScInterpreter::GetGlobalConfig();
697 aConfig
.mbOpenCLEnabled
= bEnable
;
698 ScInterpreter::SetGlobalConfig(aConfig
);
703 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */