Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / formulagroup.cxx
blobab60336670614f6581e0f19c45bb478f820799f5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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"
23 #include <vector>
24 #include <boost/unordered_map.hpp>
26 #define USE_DUMMY_INTERPRETER 0
28 #include <cstdio>
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*);
38 #endif
40 namespace sc {
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)
62 if (mpNumArray)
63 mnSize = mpNumArray->size();
64 else if (mpStrArray)
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.
73 return NULL;
75 ColArray& rCached = itColArray->second;
76 if (nSize > rCached.mnSize)
77 // Cached data array is not long enough for the requested range.
78 return NULL;
80 return &rCached;
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 =
90 maColArrays.insert(
91 ColArraysType::value_type(ColKey(nTab, nCol), ColArray(pNumArray, pStrArray)));
93 if (!r.second)
94 // Somehow the insertion failed.
95 return NULL;
97 return &r.first->second;
100 // Prior array exists for this column. Overwrite it.
101 ColArray& rArray = it->second;
102 rArray = ColArray(pNumArray, pStrArray);
103 return &rArray;
106 void FormulaGroupContext::ensureStrArray( ColArray& rColArray, size_t nArrayLen )
108 if (rColArray.mpStrArray)
109 return;
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)
119 return;
121 double fNan;
122 rtl::math::setNan(&fNan);
124 maNumArrays.push_back(
125 new sc::FormulaGroupContext::NumArrayType(nArrayLen, fNan));
126 rColArray.mpNumArray = &maNumArrays.back();
129 namespace {
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))
144 if (!pNumHead)
145 // Store the first non-NaN position.
146 pNumHead = pNum;
148 continue;
151 if (pNumHead)
153 // Flush this non-NaN segment to the matrix.
154 rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
155 pNumHead = NULL;
159 if (pNumHead)
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)
185 if (*p)
187 if (!pHead)
188 // Store the first non-empty string position.
189 pHead = p;
191 continue;
194 if (pHead)
196 // Flush this non-empty segment to the matrix.
197 flushStrSegment(rMat, nCol, pHead, p, pStrs);
198 pHead = NULL;
202 if (pHead)
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 )
211 if (!pStrs)
213 fillMatrix(rMat, nCol, pNums, nLen);
214 return;
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)
225 if (*pStr)
227 // String cell exists.
229 if (pNumHead)
231 // Flush this numeric segment to the matrix.
232 rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
233 pNumHead = NULL;
236 if (!pStrHead)
237 // Store the first non-empty string position.
238 pStrHead = pStr;
240 continue;
243 // No string cell. Check the numeric cell value.
245 if (pStrHead)
247 // Flush this non-empty string segment to the matrix.
248 flushStrSegment(rMat, nCol, pStrHead, pStr, pStrs);
249 pStrHead = NULL;
252 if (!rtl::math::isNan(*pNum))
254 // Numeric cell exists.
255 if (!pNumHead)
256 // Store the first non-NaN position.
257 pNumHead = pNum;
259 continue;
262 // Empty cell. No action required.
265 if (pStrHead)
267 // Flush the last non-empty segment to the matrix.
268 flushStrSegment(rMat, nCol, pStrHead, pStr, pStrs);
270 else if (pNumHead)
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 */)
293 return NULL;
296 bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddress& rTopPos,
297 ScFormulaCellGroupRef& xGroup,
298 ScTokenArray& rCode)
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
305 // the group.
307 ScAddress aTmpPos = rTopPos;
308 std::vector<formula::FormulaTokenRef> aResults;
309 aResults.reserve(xGroup->mnLength);
310 CachedTokensType aCachedTokens;
312 double fNan;
313 rtl::math::setNan(&fNan);
315 for (SCROW i = 0; i < xGroup->mnLength; ++i, aTmpPos.IncRow())
317 ScTokenArray aCode2;
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);
325 continue;
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;
336 double fVal = fNan;
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];
347 if (pStr)
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));
353 else
354 // Numeric cell.
355 aCode2.AddDouble(fVal);
357 break;
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())
366 nRowEnd += i;
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.
373 nRowSize = 0;
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;
387 pNums += nRowStart;
388 rtl_uString** pStrs = rArray.mpStringArray;
389 pStrs += nRowStart;
390 fillMatrix(*pMat, nCol, pNums, pStrs, nRowSize);
392 else
394 // String cells only.
395 rtl_uString** pStrs = rArray.mpStringArray;
396 pStrs += nRowStart;
397 fillMatrix(*pMat, nCol, pStrs, nRowSize);
400 else if (rArray.mpNumericArray)
402 // Numeric cells only.
403 const double* pNums = rArray.mpNumericArray;
404 pNums += nRowStart;
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);
420 else
422 ScMatrixToken aTok(pMat);
423 aCode2.AddToken(aTok);
426 break;
427 default:
428 aCode2.AddToken(*p);
432 ScFormulaCell* pDest = rDoc.GetFormulaCell(aTmpPos);
433 if (!pDest)
434 return false;
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());
446 return true;
449 #if USE_DUMMY_INTERPRETER
450 class FormulaGroupInterpreterDummy : public FormulaGroupInterpreter
452 enum Mode {
453 WRITE_OUTPUT = 0
455 Mode meMode;
456 public:
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,
471 ScTokenArray& rCode)
473 (void)rCode;
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);
482 delete [] pDoubles;
484 return true;
488 #endif
490 #ifndef DISABLE_DYNLOADING
492 class FormulaGroupInterpreterOpenCLMissing : public FormulaGroupInterpreter
494 public:
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*);
510 #endif
512 FormulaGroupInterpreter *FormulaGroupInterpreter::msInstance = NULL;
514 #ifndef DISABLE_DYNLOADING
516 osl::Module* getOpenCLModule()
518 static osl::Module aModule;
519 if (aModule.is())
520 // Already loaded.
521 return &aModule;
523 OUString aLibName(SVLIBRARY("scopencl"));
524 bool bLoaded = aModule.loadRelative(&thisModule, aLibName);
525 if (!bLoaded)
526 bLoaded = aModule.load(aLibName);
528 return bLoaded ? &aModule : NULL;
531 #endif
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"))
539 delete msInstance;
540 return msInstance = new sc::FormulaGroupInterpreterDummy();
542 #endif
544 if ( !msInstance )
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();
557 return msInstance;
560 void FormulaGroupInterpreter::fillOpenCLInfo(std::vector<OpenclPlatformInfo>& rPlatforms)
562 #ifndef DISABLE_DYNLOADING
563 osl::Module* pModule = getOpenCLModule();
564 if (!pModule)
565 return;
567 oslGenericFunction fn = pModule->getFunctionSymbol("getOpenCLPlatformCount");
568 if (!fn)
569 return;
571 size_t nPlatforms = reinterpret_cast<__getOpenCLPlatformCount>(fn)();
572 if (!nPlatforms)
573 return;
575 fn = pModule->getFunctionSymbol("fillOpenCLInfo");
576 if (!fn)
577 return;
579 std::vector<OpenclPlatformInfo> aPlatforms(nPlatforms);
580 reinterpret_cast<__fillOpenCLInfo>(fn)(&aPlatforms[0], aPlatforms.size());
581 rPlatforms.swap(aPlatforms);
582 #else
583 size_t nPlatforms = getOpenCLPlatformCount();
584 if (!nPlatforms)
585 return;
587 std::vector<OpenclPlatformInfo> aPlatforms(nPlatforms);
588 ::fillOpenCLInfo(&aPlatforms[0], aPlatforms.size());
589 rPlatforms.swap(aPlatforms);
590 #endif
593 bool FormulaGroupInterpreter::switchOpenCLDevice(const OUString& rDeviceId, bool bAutoSelect, bool bForceEvaluation)
595 bool bOpenCLEnabled = ScInterpreter::GetGlobalConfig().mbOpenCLEnabled;
596 if(!bOpenCLEnabled || rDeviceId == "Software")
598 if(msInstance)
600 // if we already have a software interpreter don't delete it
601 if(dynamic_cast<sc::FormulaGroupInterpreterSoftware*>(msInstance))
602 return true;
604 delete msInstance;
607 msInstance = new sc::FormulaGroupInterpreterSoftware();
608 return true;
610 #if HAVE_FEATURE_OPENCL
611 #ifndef DISABLE_DYNLOADING
612 osl::Module* pModule = getOpenCLModule();
613 if (!pModule)
614 return false;
616 oslGenericFunction fn = pModule->getFunctionSymbol("switchOpenClDevice");
617 if (!fn)
618 return false;
620 bool bSuccess = reinterpret_cast<__switchOpenClDevice>(fn)(&rDeviceId, bAutoSelect, bForceEvaluation);
621 if(!bSuccess)
622 return false;
623 #else
624 bool bSuccess = switchOpenClDevice(&rDeviceId, bAutoSelect, bForceEvaluation);
625 if(!bSuccess)
626 return false;
627 #endif
628 #else
629 (void) bAutoSelect;
630 #endif
632 delete msInstance;
633 msInstance = NULL;
635 #if HAVE_FEATURE_OPENCL
636 if ( ScInterpreter::GetGlobalConfig().mbOpenCLEnabled )
638 #ifdef DISABLE_DYNLOADING
639 msInstance = createFormulaGroupOpenCLInterpreter();
640 return msInstance != NULL;
641 #else
642 // Dynamically load scopencl shared object, and instantiate the opencl interpreter.
643 bSuccess = false;
644 fn = pModule->getFunctionSymbol("createFormulaGroupOpenCLInterpreter");
645 if (fn)
647 msInstance = reinterpret_cast<__createFormulaGroupOpenCLInterpreter>(fn)();
648 bSuccess = msInstance != NULL;
651 if (!msInstance)
652 msInstance = new sc::FormulaGroupInterpreterOpenCLMissing();
654 return bSuccess;
655 #endif
657 #else
658 (void) bForceEvaluation;
659 #endif
660 return false;
663 void FormulaGroupInterpreter::getOpenCLDeviceInfo(sal_Int32& rDeviceId, sal_Int32& rPlatformId)
665 rDeviceId = -1;
666 rPlatformId = -1;
667 bool bOpenCLEnabled = ScInterpreter::GetGlobalConfig().mbOpenCLEnabled;
668 if(!bOpenCLEnabled)
669 return;
671 #if HAVE_FEATURE_OPENCL
673 size_t aDeviceId = -1;
674 size_t aPlatformId = -1;
676 #ifndef DISABLE_DYNLOADING
677 osl::Module* pModule = getOpenCLModule();
678 if (!pModule)
679 return;
681 oslGenericFunction fn = pModule->getFunctionSymbol("getOpenCLDeviceInfo");
682 if (!fn)
683 return;
685 reinterpret_cast<__getOpenCLDeviceInfo>(fn)(&aDeviceId, &aPlatformId);
686 #else
687 getOpenCLDeviceInfo(&aDeviceId, &aPlatformId);
688 #endif
689 rDeviceId = aDeviceId;
690 rPlatformId = aPlatformId;
691 #endif
694 void FormulaGroupInterpreter::enableOpenCL(bool bEnable)
696 ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig();
697 aConfig.mbOpenCLEnabled = bEnable;
698 ScInterpreter::SetGlobalConfig(aConfig);
701 } // namespace sc
703 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */